﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using AEM.Data;
using AEM.Properties;

namespace AEM.Common
{
    /// <summary>
    /// Modbus协议
    /// </summary>
    public class ModbusManager : IDisposable
    {
        private static int Port = Settings.Default.Port;
        private static int TimeOut = Settings.Default.SocketTimeOut;

        private Socket socket;
        private bool isrunning = false;
        private TcpListener listener;
        public int LiftNum = 0;
        public byte[] header = new byte[5];

        /// <summary>
        /// 接收线程
        /// </summary>
        private Thread t;

        public ModbusManager()
        {
            this.Init();
        }

        private void Init()
        {
            try
            {
                isrunning = true;
                Debug.WriteLine("Ready to receive…");
                //t = new Thread(Start);
                t = new Thread(StartServer);
                t.IsBackground = true;
                t.Start();
            }
            catch { }
        }
        public Socket serverSocket = null;
        public byte[] dataBuffer = new byte[1024];
        Dictionary<string, Socket> dict = new Dictionary<string, Socket>(); //套接字集合

        public void StartServer()
        {
            try
            {
                // 地址类型;socket类型:Dgram(UDP报文) TCP流Stream;协议
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 申请端口
                IPAddress ipAddress = IPAddress.Any; // Parse把一个ip字符串转换成对象传递

                // IpAdress xx.xx.xx.xx IpEedPoint IP地址+终端号
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, Port);

                // 绑定ip和端口号
                serverSocket.Bind(ipEndPoint);

                // 开始监听端口 设置50防止服务器崩溃 设置0表示不限制数量
                serverSocket.Listen(50); // 传递挂起的连接队列的最大长度 

                // 接收客户端信息 程序会暂停，直到有一个客户端连接过来才会继续向下运行
                // Socket clientSocket = serverSocket.Accept(); // 接收一个客户端连接 返回一个Socket用来跟客户端进行通信
                serverSocket.BeginAccept(AcceptCallBack, serverSocket);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.Read();
            }
        }

        public void AcceptCallBack(IAsyncResult ar)
        {
            try
            {
                Socket serverSocket = ar.AsyncState as Socket;
                Socket clientSocket = serverSocket.EndAccept(ar);
                dict.Add(clientSocket.RemoteEndPoint.ToString(), clientSocket);

                // 持续不断的接收消息 BeginReceive()开始异步接收数据  偏移从0开始存，1024最大数量，事件方法，
                clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);

                // 接收完一个客户端之后，重新调用 继续处理下一个客户端连接
                serverSocket.BeginAccept(AcceptCallBack, serverSocket);
            }
            catch
            {

            }
        }

        public void ReceiveCallBack(IAsyncResult ar)
        {
            // 使用try catch处理客户端异常关闭终端
            // 调用完BeginReceive，表示开始监听客户端数据的传递，当它就收到一条客户端数据的时候才会去调用
            // 把clientSocket当成一个参数传递到回调方法里面，通过ar.AsyncState来获取到Socket
            Socket clientSocket = null;
            try
            {
                clientSocket = ar.AsyncState as Socket;
                clientSocket.ReceiveTimeout = 10000;
                int count = clientSocket.EndReceive(ar); // 完成数据的接收  count代表接收了多少个字节的数据
                // 如果没有接收到数据就关闭 
                if (count == 0)
                {
                    dict.Remove(clientSocket.RemoteEndPoint.ToString());
                    clientSocket.Close();
                    return;
                }
                // utf-8进行反编
                if (dataBuffer[2] == 0x00 && dataBuffer[3] == 0x00 && dataBuffer[4] == 0x00 && dataBuffer[5] == 0x06)
                {
                    int liftnum = dataBuffer[6];

                    SerialDataEventArgs args = (from s in Config.lastData
                                                where s.LiftNum == liftnum
                                                select s.Data).FirstOrDefault();

                    if (null != args)
                    {
                        byte[] data = new Byte[17];
                        data[0] = dataBuffer[0];
                        data[1] = dataBuffer[1];
                        data[2] = dataBuffer[2];
                        data[3] = dataBuffer[3];
                        data[4] = dataBuffer[4];
                        data[5] = 0x0B;
                        data[6] = byte.Parse(liftnum.ToString(), NumberStyles.Integer);
                        data[7] = 0x03;
                        data[8] = 0x08;
                        data[9] = Convert.ToByte('0');
                        data[10] = Convert.ToByte('0');
                        string tmp = string.Empty;
                        if (args.LiftInfo.LiftType != Data.Enum.LiftType.扶梯)
                        {
                            char[] c = args.FloorShow.ToCharArray();
                            if (c.Length == 1)
                            {
                                data[11] = Convert.ToByte('0');
                                data[12] = Convert.ToByte(c[0]);
                            }
                            else if(c.Length==2)
                            {
                                data[11] = Convert.ToByte(c[0]);
                                data[12] = Convert.ToByte(c[1]);
                            }
                            else if (c.Length == 3)
                            {
                                data[9] = Convert.ToByte('0');
                                data[10] = Convert.ToByte(c[0]);
                                data[11] = Convert.ToByte(c[1]);
                                data[12] = Convert.ToByte(c[2]);
                            }
                            else if (c.Length == 4)
                            {
                                data[9] = Convert.ToByte(c[0]);
                                data[10] = Convert.ToByte(c[1]);
                                data[11] = Convert.ToByte(c[2]);
                                data[12] = Convert.ToByte(c[3]);
                            }

                            tmp = "00";

                            if (args.Data.Dirver) tmp += "1";
                            else tmp += "0";
                            if (args.Data.StandAlone) tmp += "1";
                            else tmp += "0";
                            if (args.Data.FaultLamp) tmp += "1";
                            else tmp += "0";
                            if (args.Data.Locked) tmp += "1";
                            else tmp += "0";
                            if (args.Data.Fire) tmp += "1";
                            else tmp += "0";
                            if (args.Data.Overhaul) tmp += "1";
                            else tmp += "0";
                            data[13] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                            tmp = "0000";
                            if (!args.Data.DoorClose && !args.Data.DoorOpen) tmp += "10";
                            else
                            {
                                if (args.Data.DoorClose) tmp += "1";
                                else tmp += "0";
                                if (args.Data.DoorOpen) tmp += "1";
                                else tmp += "0";
                            }
                            if (args.Data.Descending) tmp += "1";
                            else tmp += "0";
                            if (args.Data.Rising) tmp += "1";
                            else tmp += "0";
                            data[14] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));
                        }
                        else
                        {
                            tmp = "0000";

                            if (Convert.ToString(args.Data.Data[41], 2).PadLeft(8, '0').Substring(2, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            tmp += "00";
                            if (Convert.ToString(args.Data.Data[40], 2).PadLeft(8, '0').Substring(7, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            data[13] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                            tmp = "000000";

                            if (Convert.ToString(args.Data.Data[41], 2).PadLeft(8, '0').Substring(0, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            if (Convert.ToString(args.Data.Data[41], 2).PadLeft(8, '0').Substring(1, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            data[14] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                            tmp = "00";
                            if (Convert.ToString(args.Data.Data[41], 2).PadLeft(8, '0').Substring(6, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            if (Convert.ToString(args.Data.Data[43], 2).PadLeft(8, '0').Substring(1, 1) == "1" ? true : false) tmp += "1";
                            else tmp += "0";
                            tmp += "0000";
                            data[16] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));
                            /*tmp = "0000";

                            if (args.Data.FaultLamp) tmp += "1";
                            else tmp += "0";
                            tmp += "00";
                            if (args.Data.Overhaul) tmp += "1";
                            else tmp += "0";
                            data[13] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                            tmp = "000000";

                            if (args.Data.Descending) tmp += "1";
                            else tmp += "0";
                            if (args.Data.Rising) tmp += "1";
                            else tmp += "0";
                            data[14] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                            tmp = "00";
                            if (args.Data.Standby) tmp += "1";
                            else tmp += "0";
                            if (args.Estop) tmp += "1";
                            else tmp += "0";
                            tmp += "0000";
                            data[16] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));*/
                        }

                        clientSocket.Send(data);
                    }
                }


                // 完成回调 一直接收数据
                clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
            }
            catch (Exception e)
            {
                // 只出异常时关闭连接
                if (clientSocket != null)
                {
                    if (dict.Count > 0)
                    {
                        dict.Remove(clientSocket.RemoteEndPoint.ToString());
                        clientSocket.Close();
                    }
                }
            }
        }

        private void Start()
        {
            //try
            //{
            IPAddress ip = IPAddress.Any;
            //提供一个 IP 地址，指示服务器应侦听所有网络接口上的客户端活动
            listener = new TcpListener(ip, Port);//初始化TcpListener实例
            listener.Start();//开启监听服务 "正处于监听状态，等待客户端连接……";
            socket = listener.AcceptSocket();//建立连接请求

            int numberOfClients = 0;
            Thread myThread = null;
            while (isrunning)
            {
                try
                {
                    //建立與客戶端的連線
                    TcpClient tmpTcpClient;
                    tmpTcpClient = listener.AcceptTcpClient();

                    if (tmpTcpClient.Connected)
                    {
                        if (null != myThread) myThread.Abort();
                        Console.WriteLine("连线成功!");
                        HandleClient handleClient = new HandleClient(tmpTcpClient);
                        myThread = new Thread(new ThreadStart(handleClient.Communicate));
                        numberOfClients += 1;
                        myThread.IsBackground = true;
                        myThread.Start();
                        myThread.Name = tmpTcpClient.Client.RemoteEndPoint.ToString();
                    }
                    else
                    {
                        tmpTcpClient.Close();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.Read();
                }
            }
        }

        public void Send(byte[] bytes)
        {
            try
            {
                socket.Send(bytes);
            }
            catch (Exception e) { Console.WriteLine(e.ToString()); }
        }

        public void Dispose()
        {
            this.socket.Close(TimeOut);
        }
    }

    public class HandleClient
    {
        /// <summary>
        /// private attribute of HandleClient class
        /// </summary>
        private TcpClient mTcpClient;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="_tmpTcpClient">傳入TcpClient參數</param>
        public HandleClient(TcpClient _tmpTcpClient)
        {
            this.mTcpClient = _tmpTcpClient;
        }

        /// <summary>
        /// Communicate
        /// </summary>
        public void Communicate()
        {
            try
            {
                CommunicationBase cb = new CommunicationBase();
                cb.ReceiveDataEvent += new CommunicationBase.ReceiveDataHandler(cb_ReceiveDataEvent);
                string msg = cb.ReceiveMsg(this.mTcpClient);
                //Console.WriteLine(msg + "\n");
                //cb.SendMsg("主機回傳測試", this.mTcpClient);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                Console.WriteLine("客户端面强制关闭连线!");
                //this.mTcpClient.Close();
                Console.Read();
            }
        }

        void cb_ReceiveDataEvent(object sender, EventArgs e)
        {
            try
            {
                int liftnum = (sender as CommunicationBase).LiftNum;

                SerialDataEventArgs args = (from s in Config.lastData
                                            where s.LiftNum == liftnum
                                            select s.Data).FirstOrDefault();

                if (null == args) return;
                byte[] data = new Byte[17];
                Buffer.BlockCopy((sender as CommunicationBase).header, 0, data, 0, 5);
                data[5] = 0x0B;
                data[6] = byte.Parse(liftnum.ToString(), NumberStyles.Integer);
                data[7] = 0x03;
                data[8] = 0x08;
                //int high = liftnum / 10;
                //int low = liftnum % 10;
                data[9] = 0x00;// byte.Parse(high.ToString(), NumberStyles.Integer);
                data[10] = 0x00;// byte.Parse(low.ToString(), NumberStyles.Integer);
                char[] c = args.FloorShow.ToCharArray();
                if (c.Length == 1)
                {
                    data[11] = Convert.ToByte('0');
                    data[12] = Convert.ToByte(c[0]);
                }
                else
                {
                    data[11] = Convert.ToByte(c[0]);
                    data[12] = Convert.ToByte(c[1]);
                }

                string tmp = "00";
                if (args.Data.Dirver) tmp += "1";
                else tmp += "0";
                if (args.Data.StandAlone) tmp += "1";
                else tmp += "0";
                if (args.Data.FaultLamp) tmp += "1";
                else tmp += "0";
                if (args.Data.Locked) tmp += "1";
                else tmp += "0";
                if (args.Data.Fire) tmp += "1";
                else tmp += "0";
                if (args.Data.Overhaul) tmp += "1";
                else tmp += "0";
                data[13] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                tmp = "0000";
                if (!args.Data.DoorClose && !args.Data.DoorOpen) tmp += "10";
                else
                {
                    if (args.Data.DoorClose) tmp += "1";
                    else tmp += "0";
                    if (args.Data.DoorOpen) tmp += "1";
                    else tmp += "0";
                }
                if (args.Data.Descending) tmp += "1";
                else tmp += "0";
                if (args.Data.Rising) tmp += "1";
                else tmp += "0";
                data[14] = Convert.ToByte(Convert.ToChar(Convert.ToInt32(tmp, 2)));

                // modbus.Send(data);
                (sender as CommunicationBase).SendMsg(data, this.mTcpClient);
            }
            catch { }
        }
        // end HandleClient()
    }

    /// <summary>
    /// CommunicationBase給客戶端和主機端共用，可傳送接收訊息
    /// </summary>
    public class CommunicationBase
    {
        public int LiftNum = 0;
        public byte[] header = new byte[5];

        public delegate void ReceiveDataHandler(object sender, EventArgs e);

        /// <summary>
        /// 接收到数据包事件
        /// </summary>
        public event ReceiveDataHandler ReceiveDataEvent;
        /// <summary>
        /// 傳送訊息
        /// </summary>
        /// <param name="msg">要傳送的訊息</param>
        /// <param name="tmpTcpClient">TcpClient</param>
        public void SendMsg(string msg, TcpClient tmpTcpClient)
        {
            NetworkStream ns = tmpTcpClient.GetStream();
            if (ns.CanWrite)
            {
                byte[] msgByte = Encoding.Default.GetBytes(msg);
                ns.Write(msgByte, 0, msgByte.Length);
            }
        }
        public void SendMsg(byte[] bytes, TcpClient tmpTcpClient)
        {
            NetworkStream ns = tmpTcpClient.GetStream();
            if (ns.CanWrite)
            {
                ns.Write(bytes, 0, bytes.Length);
            }
        }

        /// <summary>
        /// 接收訊息
        /// </summary>
        /// <param name="tmpTcpClient">TcpClient</param>
        /// <returns>接收到的訊息</returns>
        public string ReceiveMsg(TcpClient tmpTcpClient)
        {
            string receiveMsg = string.Empty;
            byte[] receiveBytes = new byte[tmpTcpClient.ReceiveBufferSize];
            int numberOfBytesRead = 0;
            NetworkStream ns = tmpTcpClient.GetStream();

            if (ns.CanRead)
            {
                do
                {
                    numberOfBytesRead = ns.Read(receiveBytes, 0, tmpTcpClient.ReceiveBufferSize);

                    if (numberOfBytesRead < 12) continue;
                    if (!(receiveBytes[2] == 0x00 && receiveBytes[3] == 0x00 && receiveBytes[4] == 0x00 && receiveBytes[5] == 0x06)) continue;
                    byte[] lenb = new byte[12];
                    Buffer.BlockCopy(receiveBytes, 0, lenb, 0, 12);

                    Buffer.BlockCopy(receiveBytes, 0, header, 0, 5);
                    if (receiveBytes.Length > 10) this.LiftNum = receiveBytes[6];

                    ReceiveDataEvent.BeginInvoke(this, new EventArgs(), new AsyncCallback(Callback), null);
                }
                while (true);
            }
            return receiveMsg;
        }
        private void Callback(IAsyncResult tag)
        {
            AsyncResult result = (AsyncResult)tag;
            ReceiveDataHandler del = (ReceiveDataHandler)result.AsyncDelegate;
            del.EndInvoke(tag);
        }
    }
}
